import logging
from dataclasses import asdict
from uuid import UUID

import httpx

from weed_shop.core.models.dto.product import ProductForSyncDTO
from weed_shop.infrastructure.loyverse.api_client.schemas import Category, LoyverseReceipt, Product

logger = logging.getLogger(__name__)


class LoyverseClient:
    def __init__(self, auth_token: str, store_id: str, logfile_path: str):
        self._base_url = f"https://api.loyverse.com/v1.0"
        self._auth_token = auth_token
        self.loyverse_store_id = store_id
        self.logfile_path = logfile_path
        self._configure_logging()

    def _configure_logging(self):
        self.logger = logging.getLogger("loyverse_client")
        self.logger.setLevel(logging.INFO)

        log_handler = logging.FileHandler(self.logfile_path)
        log_handler.setLevel(logging.INFO)

        log_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
        log_handler.setFormatter(log_formatter)

        self.logger.addHandler(log_handler)

    def _create_http_client(self) -> httpx.AsyncClient:
        return httpx.AsyncClient(base_url=self._base_url, headers={"Authorization": f"Bearer {self._auth_token}"})

    async def _send_request(self, method: str, url: str, **kwargs) -> httpx.Response | None:
        async with self._create_http_client() as client:
            try:
                response = await client.request(method, url, **kwargs)
                self._log_response(response, request_body=str(kwargs.get("json")))
                response.raise_for_status()
                return response
            except httpx.HTTPStatusError as e:
                self.logger.info(f"Request failed: {e.response.json()}")
                logger.error(f"Request to {url} with method {method} failed: {e.response.text}")
                return None

    async def _get_categories(self) -> list[Category] | None:
        url = f"{self._base_url}/categories/"
        response = await self._send_request("GET", url, params={"limit": 250})
        if response:
            return [Category(**category) for category in response.json().get("categories")]
        return None

    async def get_category(self, category_name: str) -> Category | None:
        """Получает категорию"""

        categories = await self._get_categories()
        for category in categories:
            if category.name == category_name:
                return category

        self.logger.info(f"Failed to find category with {category_name = }")
        logger.info(f"Failed to find category with {category_name = }")
        return None

    async def create_category(self, category_name: str) -> Category | None:
        """Создает категорию"""

        url = f"{self._base_url}/categories"
        data = {"name": category_name}

        response = await self._send_request("POST", url, json=data)
        if response:
            category = Category(**response.json())
            self.logger.info(f"Created new category with id = {category.id}")
            return category
        return None

    async def _get_products(self) -> list[Product] | None:
        url = f"{self._base_url}/items/"
        response = await self._send_request("GET", url, params={"limit": 250})  # TODO Заменить на пагинацию
        if response:
            return [Product(**product) for product in response.json().get("items")]
        return None

    async def get_product(self, product_sku: str) -> Product | None:
        """Получает товар в loyverse по sku товара"""

        products = await self._get_products()
        for product in products:
            for product_variant in product.variants:
                if product_variant.sku == product_sku:
                    return product
        self.logger.info(f"Failed to find product with sku = {product_sku}")
        logger.info(f"Failed to find product with sku = {product_sku}")
        return None

    async def create_or_update_product(
        self, bot_product: ProductForSyncDTO, category_id: UUID, item_id: UUID | None = None
    ) -> Product | None:
        """Создание/обновление товара в loyverse из товара в боте"""

        url = f"{self._base_url}/items"
        data = {
            "item_name": bot_product.title,
            "item_description": bot_product.description,
            "reference_id": bot_product.sku,
            "category_id": str(category_id),
            "track_stock": True,
            "variants": [
                {
                    "sku": bot_product.sku,
                    "default_pricing_type": "VARIABLE",
                    "stores": [
                        {
                            "store_id": self.loyverse_store_id,
                            "pricing_type": "FIXED",
                            "price": bot_product.retail_price,
                            "available_for_sale": True,
                        },
                    ],
                }
            ],
        }
        # Если передан item_id, происходит обновление товара в loyverse
        if item_id:
            data["id"] = str(item_id)

        response = await self._send_request("POST", url, json=data)
        if response:
            product = Product(**response.json())
            if item_id:
                self.logger.info(
                    f"Updated product with product_id = {product.id}. New values: {str(asdict(bot_product))}"
                )
            else:
                self.logger.info(
                    f"Created new product with product_id = {product.id}. Values: {str(asdict(bot_product))}"
                )
            return product
        return None

    async def update_product_variant_stock(self, variant_id: UUID, new_stock_amount: int) -> None:
        """Обновление товара в loyverse из товара в боте"""

        url = f"{self._base_url}/inventory"
        data = {
            "inventory_levels": [
                {"variant_id": str(variant_id), "store_id": self.loyverse_store_id, "stock_after": new_stock_amount}
            ]
        }
        response = await self._send_request("POST", url, json=data)
        if not response.is_success:
            self.logger.info(f"Failed to update variant stock in Loyverse: {response.json()}")
            return
        self.logger.info(f"Updated stock in variant with variant_id = {variant_id}. New value: {new_stock_amount}")

    async def get_sales(self) -> list[LoyverseReceipt]:
        """Забирает продажи из loyverse"""

        url = f"{self._base_url}/receipts"
        response = await self._send_request("GET", url)
        if response and response.json().get("receipts"):
            return [LoyverseReceipt(**receipt) for receipt in response.json().get("receipts")]
        logger.info("Failed to get receipts")
        self.logger.info(f"Failed to get receipts")
        return []

    @staticmethod
    def _log_response(response: httpx.Response, request_body: str | None = None) -> None:
        """Логирует ответ сервера"""

        logger.info(f"{response.url} - {response.status_code}")
        logger.info(response.text)
        if request_body:
            logger.info(f"Request body {request_body}")
